package mycassandra

import (
	"fmt"
	"github.com/gocql/gocql"
	"log"
	"net"
	"net/url"
	"os"
	"time"
)

var serIp string = GetLocalIP()

// Cassandra config list
const KeySpace string = "msgkeyspace"
const user string     = "cassandra"
const password string = "cassandra"
const numCons int     = 500

var hosts = []string{
	"172.16.175.245",
}

// CQL Session
var cqlSession = getCQLSession()

/**
* Get CQL session
*
* @author Moks
 */
func getCQLSession() *gocql.Session{
	// connect to the cluster config
	clusterConfig := gocql.NewCluster(hosts...)
	clusterConfig.Consistency    = gocql.One
	clusterConfig.ProtoVersion   = 4
	clusterConfig.Keyspace       = KeySpace
	clusterConfig.NumConns       = numCons
	clusterConfig.ConnectTimeout = time.Second * 100
	clusterConfig.Authenticator  = gocql.PasswordAuthenticator{Username: user, Password: password}
	clusterConfig.DisableInitialHostLookup = true
	_session, err := clusterConfig.CreateSession()

	if err != nil {
		log.Fatal(err.Error())
	}
	_session.SetTrace(gocql.NewTraceWriter(_session, os.Stdout))
	return _session
}

/**
* Insert msg

* @param appId
* @param msg
* @author Moks
*/
func Insert(appId string,ownerId string,typeId string, msg string,currentTime time.Time,sign string) {

	currentTimeStr := currentTime.Format("2006-01-02 15:04:05")
	if err := cqlSession.Query(`INSERT INTO msg (app_id,owner_id,type_id,msg_id,msg,sign,ser_ip,created_stamp) 
                                      VALUES (?, ?, ?, now(), ?,? ,?,?)`,
		appId, ownerId,typeId, msg,sign,serIp,currentTimeStr).Exec(); err != nil {
		log.Fatal(err)
	}
}


/**
* Search msg
* 1. Search data
* 2. Delete data
* 4. Return data of JSON
*
* @param appId
* @param msg
* @param action  delete: delete，keep:keep
*
* @return [] = no data, else [{}]=exist data
*
* @author Moks
 */
func Search(appId string,quantity int, action string) string {

    // 1. Search data
	dataResult := ""
	var ownerId,typeId,msgId,msg,sign,serIp,createdStamp string
	iter := cqlSession.Query(`select app_id,owner_id,type_id,msg_id,msg,sign,ser_ip,created_stamp from msg where app_id=? order by msg_id asc limit ?`,
		    appId,quantity).Iter()


	// Create data
	rowCount := iter.NumRows()
	msgIds   := make([]string, rowCount)

	index :=0
	for iter.Scan(&appId,&ownerId,&typeId,&msgId,&msg,&sign,&serIp,&createdStamp) {

		msgStr := url.QueryEscape(msg)
		dataResult +="{"
		dataResult += "\"appId\":\""+appId+"\",\"ownerId\":\""+ownerId+"\",\"typeId\":\""+typeId+"\",\"msgId\":\""
		dataResult += msgId+"\",\"msg\":\""+msgStr+"\",\"sign\":\""+sign+"\",\"serIp\":\""+serIp+"\",\"createdStamp\":\""+createdStamp+"\""

		fmt.Println("index=",index)
		msgIds[index] = msgId

		if index < (rowCount-1){
			dataResult +="},"
		}else{
			dataResult +="}"
		}
		index ++
	}

    // Close iteration
	if err := iter.Close(); err != nil {
		log.Fatal(err)
	}

	// 2. Delete data
	if "delete" == action{
		DeleteMsg(appId,msgIds)
	}


	// 4. Return data of JSON
	if "" == dataResult{
		dataResult = "[]"
	}else{
		dataResult = "["+dataResult+"]"
	}
    return dataResult
}

/**
* Delete msg
*
* 1. Checking that msgIds length = 0
* 2. Execute deleting action
*/
func DeleteMsg(appId string,msgIds []string){

	// 1. Checking that msgIds length = 0
	if len(msgIds) == 0{
		return
	}

	// 2. Execute delete action
	query := fmt.Sprintf("DELETE FROM msg WHERE app_id=? and msg_id in(%s)",CreateQuestionMarks(len(msgIds)))
	args  := ConvertToInterfaces(appId,msgIds)

	if err := cqlSession.Query(query,args...).Exec(); err != nil {
		fmt.Println("Error while deleting msg")
		fmt.Println(err)
	}
}


func CreateQuestionMarks(n int) string {
	s := ""
	for i := 0; i < n; i++ {
		s += "?"
		if i < (n-1){
			s += ","
		}
	}
	return s
}


func ConvertToInterfaces(appId string,slice []string) []interface{} {
	s := make([]interface{}, len(slice)+1)
	s[0] = appId
	for i, v := range slice {
		s[i+1] = v
	}
	return s
}

/**
* Get local IP
 */
func GetLocalIP() string {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return ""
	}
	for _, address := range addrs {
		// check the address type and if it is not a loopback the display it
		if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				return ipnet.IP.String()
			}
		}
	}
	return ""
}
